package MT::TemplateFile;
use strict;
use warnings;

use vars qw( $VERSION);
$VERSION = 0.032;

use MT::Builder;
use MT::ConfigMgr;
use MT::FileMgr;
use File::Spec;

use MT::ErrorHandler;
@MT::TemplateFile::ISA = qw( MT::ErrorHandler );

sub new { bless {}, $_[0]; }

sub file_mgr {
    my $tmpl = shift;
    $tmpl->{__file_mgr} = MT::FileMgr->new('Local')
      unless exists $tmpl->{__file_mgr};
    $tmpl->{__file_mgr};
}

sub load {
    my $class = shift;
    my $file  = shift;
    my $tmpl  = $class->new;
    my $fmgr  = $tmpl->file_mgr;
    $tmpl->filename($file);
    $tmpl->{__text} = $fmgr->get_data($file)
      or return
      MT::TemplateFile->error(
                              MT->translate(
                                         "Load of template '[_1]' failed: [_2]",
                                         $file, $fmgr->errstr
                              )
      );
    $tmpl;
}

sub build {
    my $tmpl = shift;
    my ($ctx, $cond) = @_;
    my $text   = MT->translate_templatized($tmpl->text);
    my $build  = MT::Builder->new;
    my $tokens = $build->compile($ctx, $text)
      or return
      $tmpl->error(
          MT->translate(
                        "Parse error in template '[_1]': [_2]", $tmpl->filename,
                        $build->errstr
          )
      );
    defined(my $res = $build->build($ctx, $tokens, $cond))
      or return
      $tmpl->error(
          MT->translate(
                        "Build error in template '[_1]': [_2]", $tmpl->filename,
                        $build->errstr
          )
      );
    $res;
}

sub save {
    my $fmgr = $_[0]->file_mgr;
    $fmgr->put_data($_[0]->text, $_[0]->filename)
      or return
      $_[0]->error(
                MT->translate(
                              "Writing to '[_1]' failed: [_2]", $_[0]->filename,
                              $fmgr->errstr
                )
      );
}

sub remove {
    unlink($_[0]->filename)
      or return $_[0]->error(
          MT->translate("Removing '[_1]' failed: [_2]", $_[0]->filename, "$!"));

    # does this localized message exist?
}

sub text { $_[0]->{__text} = $_[1] if defined $_[1]; $_[0]->{__text}; }
sub filename { $_[0]->{__file}; }

sub publish {
    my $tmpl    = shift;
    my $outfile = shift
      or return
      $tmpl->error(
                  MT->translate(
                                "Template '[_1]' does not have an Output File.",
                                $tmpl->{__file}
                  )
      );
    my $ctx  = shift;
    my $cond = shift;
    my $html = $tmpl->build($ctx, $cond);
    return MT->error($tmpl->errstr) unless defined $html;

    # unless (File::Spec->file_name_is_absolute($outfile)) {
    #	$outfile = File::Spec->catfile($site_root, $outfile);
    # }
    ($outfile) = $outfile =~ /(.+)/s;
    my $fmgr = $tmpl->file_mgr;
    return 1 unless $fmgr->content_is_updated($outfile, \$html);
    my $use_temp_files = !MT::ConfigMgr->instance->NoTempFiles;
    my $temp_file = $use_temp_files ? "$outfile.new" : $outfile;
    defined($fmgr->put_data($html, $temp_file))
      or return
      $tmpl->error(
                   MT->translate(
                                 "Writing to '[_1]' failed: [_2]", $temp_file,
                                 $fmgr->errstr
                   )
      );
    if ($use_temp_files) {
        $fmgr->rename($temp_file, $outfile)
          or return
          $tmpl->error(
              MT->translate(
                            "Renaming tempfile '[_1]' failed: [_2]", $temp_file,
                            $fmgr->errstr
              )
          );
    }
    1;
}

1;

__END__

=head1 NAME

MT::TemplateFile - Movable Type page generation from a
template file

=head1 SYNOPSIS

	use MT::TemplateFile;
	
	# create a new template.
	my $tmpl = MT::Template->new;
	$tmpl->text( $mt_layout );
	$tmpl->filename('/path/to/file/template');
	$tmpl->save;
	
	# outputs content from a file template
	my $tmpl = MT::Template->load('/path/to/file/template');
	require MT::Template::Context;
	my $ctx = MT::Template::Context->new;
	$ctx->stash('blog_id',2);
	print $tmpl->build($ctx); # to screen
	$tmpl->publish($ctx, '/path/to/output/file');
	
	# removes the template file
	$tmpl->remove;
	

=head1 DESCRIPTION

An I<MT::TemplateFile> provides the necessary mechanism for
publishing a page from a template from a local file as
opposed to a I<MT::Template> object. This is helpful for
applications where the exact template location must be known
or where an application uses an MT template that does not
belong to a specific weblog.

In the past using MT templates in an application has been
problematic because templates are associated to a specific
weblog. A user would have to install a template in a
specific weblog and name it something specific. If the name
of the template to differ even slightly the application
would not work properly. Being reading directly from the
file system, this module avoids [the middle man] and lets
you hide the template from browser users.

This module can also be used with a I<MT::Template> that is
linked to an external file where any updates to the template
through the Movable Type CMS will be synced automatically to
the file on disk, and vice versa.

For the most part, I<MT::TemplateFile> mimics the interface
of I<MT::Template> with an added method of publish which is
similar to the I<rebuild> method in the MT module.

=head1 USAGE

Unlike I<MT::Template>, I<MT::TemplateFile> is B<not> a
subclass of I<MT::Object>. It does provide similar methods
in working with its file based data. I<MT::TemplateFile> has
the following interface:

=head2 MT::TemplateFile->new

Constructs a new I<MT::TemplateFile> instance and returns
that object. Returns C<undef> on failure.

=head2 MT::TemplateFile->load( $file )

Constructs a new I<MT::TemplateFile> instance, loads
I<$file> and returns that object. Returns C<undef> on
failure.

=head2 $tmpl->filename( $file )

The name/path of the template file. Note that filename is
used to save a MT::TemplateFile, but is not saved in the
template itself.

=head2 $tmpl->text( $str )

The body of the template, containing the markup and Movable
Type template tags. Sets the template text with the value of
I<$str> if supplied.

=head2 $tmpl->save

Writes the current template C<text> to the location
specified by C<filename>.

=head2 $tmpl->remove

Remove the template file from the file system.

=head2 $tmpl->build($ctx [, \%cond ])

Given a context I<$ctx> (an I<MT::Template::Context> object)
and an optional set of conditions \%cond, builds a template
into its output form.

See the I<MT::Template> object C<build> method documentation
for more.

=head2 $tmpl->publish( $outfile, $ctx [, \%cond ])

Given a context I<$ctx> (an I<MT::Template::Context> object)
and an optional set of conditions \%cond, executes a
C<build> and write the result to the I<$outfile>. Publishes
using temp files based on the parameters specified in
Movable Type's F<mt.cfg> configuration file.

See the I<MT::Template> object C<build> method documentation
for more.

=head1 INSTALLATION

Drop F<TemplateFile.pm> in the F<extlib/MT> of your MT
application directory. You may have to create the F<MT>
directory in F<extlib>.

=head1 TO DO

Nothing specific though these are some areas that may be
useful. Only usage and feedback will tell.

=over4

=item * Better context management?

=item * Is their a localized message that can use with
remove errors?

=item * Add page caching mechanism to speed content
generation in dynamic application environments?

=back

=head1 AUTHOR & COPYRIGHT

MT::TemplateFile is Copyright 2004-2005, Timothy Appnel,
Appnel Internet Solutions, LLC. http://appnel.com/

This program is free software; you can use, modify, and
redistribute it under the same terms as Perl 5.8.x itself.

See http://www.perl.com/perl/misc/Artistic.html

=cut
